<template>
  <!-- 创建容器 -->
  <div id="mountNode"></div>
</template>

<script>
// 导入 G6
import G6 from '@antv/g6';

export default {
  mounted() {
    // 缩略图 (Minimap) 是一种常见的用于快速预览和探索图的工具，可作为导航辅助用户探索大规模图。
    // 实例化 minimap 插件
    const minimap = new G6.Minimap({
      size: [100, 100],
      className: 'minimap',
      type: 'delegate',
    });
    // 网格可用于辅助用户在拖拽节点时对齐到网格
    // 实例化 grid 插件
    const grid = new G6.Grid();

    const graph = new G6.Graph({
      container: 'mountNode',
      width: 800,
      height: 600,
      // 是否将图适配到画布大小，可以防止超出画布或留白太多
      //fitView: true, //与layout同时使用时去掉这个特性，复杂的布局系统会打破适配的规则，反而会造成更多的困扰
      // 画布上的四周留白宽度
      fitViewPadding: [20, 40, 50, 20],

      // 节点在默认状态下的样式配置（style）和其他配置
      defaultNode: {
        size: 30, // 节点大小
        // ...                 // 节点的其他配置
        // 节点样式配置
        style: {
          fill: 'steelblue', // 节点填充色
          stroke: '#666', // 节点描边色
          lineWidth: 1, // 节点描边粗细
        },
        // 节点上的标签文本配置
        labelCfg: {
          // 节点上的标签文本样式配置
          style: {
            fill: '#fff', // 节点标签文字颜色
          },
        },
      },
      // 边在默认状态下的样式配置（style）和其他配置
      defaultEdge: {
        // ...                 // 边的其他配置
        // 边样式配置
        style: {
          opacity: 0.6, // 边透明度
          stroke: 'grey', // 边描边颜色
        },
        // 边上的标签文本配置
        labelCfg: {
          autoRotate: true, // 边上的标签文本根据边的方向旋转
        },
      },

      //图布局 Layout
      layout: {
        // Object，可选，布局的方法及其配置项，默认为 random 布局。
        // type: 'force', // 指定为力导向布局
        type: 'force2', // 指定为力导向布局
        //animate: true, // 是否每次迭代都刷新画布, force需开启避免页面有卡顿感, force2开启后抖动过于频繁
        preventOverlap: true, // 防止节点重叠
        // nodeSize: 30        // 节点大小，用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性，则不需要在此设置 nodeSize。
        linkDistance: 50, // 指定边距离
      },

      //交互行为 Behavior
      modes: {
        // 允许拖拽画布、放缩画布、拖拽节点
        default: ['drag-node', 'drag-canvas', 'zoom-canvas',
          // 点提示框交互工具的配置
          {
            type: 'tooltip',
            formatText(model) {
              const text = 'label: ' + model.label
                + '<br/> class: ' + model.class;
              return text;
            },
            shouldUpdate: e => {
              return true;
            }
          },
          // 边提示框交互工具的配置
          {
            type: 'edge-tooltip',
            formatText(model) {
              const text = 'source: ' + model.source
                + '<br/> target: ' + model.target
                + '<br/> weight: ' + model.weight;
              return text;
            },
            shouldUpdate: e => {
              return true;
            }
          }
        ]
      },

      // 交互状态 State
      // 节点不同状态下的样式集合
      // 样式 + 点击事件绑定 => 点击图后样式变化
      nodeStateStyles: {
        // 鼠标 hover 上节点，即 hover 状态为 true 时的样式
        hover: {
          fill: 'lightsteelblue',
        },
        // 鼠标点击节点，即 click 状态为 true 时的样式
        click: {
          stroke: '#000',
          lineWidth: 3,
        },
      },
      // 边不同状态下的样式集合
      edgeStateStyles: {
        // 鼠标点击边，即 click 状态为 true 时的样式
        click: {
          stroke: 'steelblue',
          //stroke: '#CCE8CF',
        },
      },


      plugins: [minimap, grid], // 将 minimap, grid 实例配置到图上

    });

    const localData = {
      "nodes": [
        {"id": "0", "label": "n0", "class": "c0", "x": 1000, "y": -100},
        {"id": "1", "label": "n1", "class": "c0", "x": 300, "y": -10},
        {"id": "2", "label": "n2", "class": "c0", "x": 10, "y": 10},
        {"id": "3", "label": "n3", "class": "c0", "x": 320, "y": -100},
        {"id": "4", "label": "n4", "class": "c0", "x": 100, "y": 900},
        {"id": "5", "label": "n5", "class": "c0", "x": 120, "y": 213},
        {"id": "6", "label": "n6", "class": "c1", "x": 543, "y": 12},
        {"id": "7", "label": "n7", "class": "c1", "x": 543, "y": -100},
        {"id": "8", "label": "n8", "class": "c1", "x": 1, "y": 0},
        {"id": "9", "label": "n9", "class": "c1", "x": 0, "y": -222},
        {"id": "10", "label": "n10", "class": "c1", "x": 435, "y": 69},
        {"id": "11", "label": "n11", "class": "c1", "x": 23, "y": 10},
        {"id": "12", "label": "n12", "class": "c1", "x": -129, "y": 39},
        {"id": "13", "label": "n13", "class": "c2", "x": 234, "y": 843},
        {"id": "14", "label": "n14", "class": "c2", "x": -301, "y": 129},
        {"id": "15", "label": "n15", "class": "c2", "x": -20, "y": -76},
        {"id": "16", "label": "n16", "class": "c2", "x": 1220, "y": -34},
        {"id": "17", "label": "n17", "class": "c2", "x": -10, "y": 954},
        {"id": "18", "label": "n18", "class": "c2", "x": 492, "y": 123},
        {"id": "19", "label": "n19", "class": "c2", "x": 123, "y": -241}
      ],
      "edges": [
        {"source": "0", "target": "1", "label": "e0-1", "weight": 1},
        {"source": "0", "target": "2", "label": "e0-2", "weight": 2},
        {"source": "0", "target": "3", "label": "e0-3", "weight": 3},
        {"source": "0", "target": "4", "label": "e0-4", "weight": 1.4},
        {"source": "0", "target": "5", "label": "e0-5", "weight": 2},
        {"source": "0", "target": "7", "label": "e0-7", "weight": 2},
        {"source": "0", "target": "8", "label": "e0-8", "weight": 2},
        {"source": "0", "target": "9", "label": "e0-9", "weight": 1.3},
        {"source": "0", "target": "10", "label": "e0-10", "weight": 1.5},
        {"source": "0", "target": "11", "label": "e0-11", "weight": 1},
        {"source": "0", "target": "13", "label": "e0-13", "weight": 10},
        {"source": "0", "target": "14", "label": "e0-14", "weight": 2},
        {"source": "0", "target": "15", "label": "e0-15", "weight": 0.5},
        {"source": "0", "target": "16", "label": "e0-16", "weight": 0.8},
        {"source": "2", "target": "3", "label": "e2-3", "weight": 1},
        {"source": "4", "target": "5", "label": "e4-5", "weight": 1.4},
        {"source": "4", "target": "6", "label": "e4-6", "weight": 2.1},
        {"source": "5", "target": "6", "label": "e5-6", "weight": 1.9},
        {"source": "7", "target": "13", "label": "e7-13", "weight": 0.5},
        {"source": "8", "target": "14", "label": "e8-14", "weight": 0.8},
        {"source": "9", "target": "10", "label": "e9-10", "weight": 0.2},
        {"source": "10", "target": "14", "label": "e10-14", "weight": 1},
        {"source": "10", "target": "12", "label": "e10-12", "weight": 1.2},
        {"source": "11", "target": "14", "label": "e11-14", "weight": 1.2},
        {"source": "12", "target": "13", "label": "e12-13", "weight": 2.1},
        {"source": "16", "target": "17", "label": "e16-17", "weight": 2.5},
        {"source": "16", "target": "18", "label": "e16-18", "weight": 3},
        {"source": "17", "target": "18", "label": "e17-18", "weight": 2.6},
        {"source": "18", "target": "19", "label": "e18-19", "weight": 1.6}
      ]
    }

    const main = async () => {
      /*const response = await fetch(
        'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
      );
      const remoteData = await response.json();
      graph.data(remoteData);*/

      // 可以直接将配置写入数据文件(后端处理)；也可以在读入数据后，通过遍历的方式写入配置(前端处理)。
      // 遍历点数据
      localData.nodes.forEach((node) => {
        if (!node.style) {
          node.style = {};
        }
        switch (
          node.class // 根据节点数据中的 class 属性配置图形
          ) {
          case 'c0': {
            node.type = 'circle'; // class = 'c0' 时节点图形为 circle
            break;
          }
          case 'c1': {
            node.type = 'rect'; // class = 'c1' 时节点图形为 rect
            node.size = [35, 20]; // class = 'c1' 时节点大小
            break;
          }
          case 'c2': {
            node.type = 'ellipse'; // class = 'c2' 时节点图形为 ellipse
            node.size = [35, 20]; // class = 'c2' 时节点大小
            break;
          }
        }
      });


      // 遍历边数据
      localData.edges.forEach((edge) => {
        if (!edge.style) {
          edge.style = {};
        }
        edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
        // opacity 文本透明度
        edge.style.opacity = 0.6;
        edge.style.stroke = 'grey';
      });

      graph.data(localData);

      graph.render();


      // 点击事件绑定
      // 监听鼠标进入节点
      graph.on('node:mouseenter', (e) => {
        const nodeItem = e.item;
        // 设置目标节点的 hover 状态 为 true
        graph.setItemState(nodeItem, 'hover', true);
      });
      // 监听鼠标离开节点
      graph.on('node:mouseleave', (e) => {
        const nodeItem = e.item;
        // 设置目标节点的 hover 状态 false
        graph.setItemState(nodeItem, 'hover', false);
      });
      // 监听鼠标点击节点
      graph.on('node:click', (e) => {
        // 1. 清理之前点击过的节点状态
        // 先将所有当前有 click 状态的节点的 click 状态置为 false
        const clickNodes = graph.findAllByState('node', 'click');
        clickNodes.forEach((cn) => {
          graph.setItemState(cn, 'click', false);
        });

        // 2. 当前节点状态修改
        const nodeItem = e.item;
        // 设置目标节点的 click 状态 为 true
        graph.setItemState(nodeItem, 'click', true);
      });
      // 监听鼠标点击节点
      graph.on('edge:click', (e) => {
        // 先将所有当前有 click 状态的边的 click 状态置为 false
        const clickEdges = graph.findAllByState('edge', 'click');
        clickEdges.forEach((ce) => {
          graph.setItemState(ce, 'click', false);
        });
        const edgeItem = e.item;
        // 设置目标边的 click 状态 为 true
        graph.setItemState(edgeItem, 'click', true);
      });

    };


    main();
  }
}
</script>


<style scoped>

.g6-tooltip {
  border: 1px solid #e2e2e2;
  border-radius: 4px;
  font-size: 12px;
  color: #545454;
  background-color: rgba(255, 255, 255, 0.9);
  padding: 10px 8px;
  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
</style>
